package socketmvc.core.processor.bean;

import socketmvc.core.context.IContext;
import socketmvc.core.packets.basic.IPacket;
import socketmvc.core.packets.biz.SockFile;
import socketmvc.core.packets.biz.SockMultiFile;
import socketmvc.core.processor.bean.optimizer.ret.*;
import socketmvc.core.util.ClassUtils;
import socketmvc.core.util.StringUtils;
import java.io.File;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;

public class ProcessorMethodReturn {

    private String returnName;

    private Type returnType;

    private Class<?> returnClass;

    private ValOptimizer optimizer;

    public ProcessorMethodReturn() {}

    public ProcessorMethodReturn(String returnName,  Class<?> returnClass , Type returnType) {
        this.returnName = returnName;
        this.returnType = returnType;
        this.returnClass = returnClass;
        this.optimizer = getValOptimizer(this);
    }

    public static ValOptimizer getValOptimizer(ProcessorMethodReturn methodReturn) {
        String returnName = methodReturn.getReturnName();
        Type returnType = methodReturn.getReturnType();
        Class<?> returnClass = methodReturn.getReturnClass();

        if (methodReturn.isVoid()){
            return null;
        }

        ValOptimizer optimizer;
        if (ClassUtils.classAssignableFrom(returnClass, IPacket.class)) {
            optimizer = new DefRetPacketValOptimizer();
        } else if (returnClass.equals(SockFile.class) ) {
            optimizer = new DefRetSockFileValOptimizer();
        } else if (returnClass.equals(File.class)) {
            optimizer = new DefRetFileValOptimizer();
        } else if (returnClass.equals(SockMultiFile.class)) {
            optimizer = new DefRetSockMultiFileValOptimizer();
        } else {

            if (returnClass.isArray()){
                Class<?> componentType = returnClass.getComponentType();
                if (componentType.equals(SockFile.class)){
                    optimizer = new DefRetSockFileArrValOptimizer();
                    return optimizer;
                }
                if (componentType.equals(File.class)){
                    optimizer = new DefRetFileArrValOptimizer();
                    return optimizer;
                }
                if (ClassUtils.classAssignableFrom(componentType, IPacket.class)){
                    optimizer = new NotSupportReturnTypeValOptimizer(returnClass);
                    return optimizer;
                }
            }

            if (ClassUtils.classAssignableFrom(returnClass, Collection.class)){
                ParameterizedType pType =  ((ParameterizedType)returnType);
                Class<?> actualTypeArgument = (Class<?>) pType.getActualTypeArguments()[0];
                if (actualTypeArgument.equals(SockFile.class)){
                    optimizer = new DefRetSockFileCollectionValOptimizer();
                    return optimizer;
                }
                if (actualTypeArgument.equals(File.class)){
                    optimizer = new DefRetFileCollectionValOptimizer();
                    return optimizer;
                }
                if (ClassUtils.classAssignableFrom(actualTypeArgument, IPacket.class)){
                    optimizer = new NotSupportReturnTypeValOptimizer(returnType);
                    return optimizer;
                }
            }

            if (StringUtils.isEmpty(returnName)) {
                optimizer = new DefRetSerializableObjectValOptimizer();
            } else {
                optimizer = new DefRetSerializableObjectValResponseOptimizer(returnName);
            }
//            if (returnClass.equals(String.class)
//                    || ClassUtils.classAssignableFrom(returnClass, Number.class)
//                    || returnClass.isPrimitive()) {
//            }

        }
        return optimizer;
    }

    public String getReturnName() {
        return returnName;
    }

    public void setReturnName(String returnName) {
        this.returnName = returnName;
    }

    public Type getReturnType() {
        return returnType;
    }

    public void setReturnType(Type returnType) {
        this.returnType = returnType;
    }

    public Class<?> getReturnClass() {
        return returnClass;
    }

    public void setReturnClass(Class<?> returnClass) {
        this.returnClass = returnClass;
    }

    public boolean isVoid(){
        return Void.TYPE.equals(returnType);
    }

    public ValOptimizer getOptimizer() {
        return optimizer;
    }

    public void setOptimizer(ValOptimizer optimizer) {
        this.optimizer = optimizer;
    }

    public static abstract class ValOptimizer{
        public abstract IPacket value(IContext context, IPacket packet,Object returnVal);
    }
}
